package io.noties.markwon.plugin.typogram.bean


/**
 *
 * @author Efstathios Sideris
 */
class AbstractionGrid private constructor(width: Int, height: Int) {
    private var grid: TextGrid

    /**
     * Makes an AbstractionGrid using the `cellSet`
     * of `textGrid`.
     *
     * @param textGrid
     * @param cellSet
     */
    constructor(textGrid: TextGrid, cellSet: CellSet) : this(textGrid.width, textGrid.height) {
        /*this(cellSet.getWidth(), cellSet.getHeight());

		cellSet = new CellSet(cellSet);
		cellSet.translate( - cellSet.getMinX(), - cellSet.getMinY());*/if (DEBUG) {
            println("Making AbstractionGrid using buffer:")
            textGrid.printDebug()
            println("...and the following CellSet:")
            cellSet.printAsGrid()
        }
        val it: Iterator<*> = cellSet.iterator()
        while (it.hasNext()) {
            val cell = it.next() as TextGrid.Cell
            if (textGrid.isBlank(cell)) continue
            if (textGrid.isCross(cell)) {
                set(cell.x, cell.y, AbstractCell.makeCross())
            } else if (textGrid.isT(cell)) {
                set(cell.x, cell.y, AbstractCell.makeT())
            } else if (textGrid.isK(cell)) {
                set(cell.x, cell.y, AbstractCell.makeK())
            } else if (textGrid.isInverseT(cell)) {
                set(cell.x, cell.y, AbstractCell.makeInverseT())
            } else if (textGrid.isInverseK(cell)) {
                set(cell.x, cell.y, AbstractCell.makeInverseK())
            } else if (textGrid.isCorner1(cell)) {
                set(cell.x, cell.y, AbstractCell.makeCorner1())
            } else if (textGrid.isCorner2(cell)) {
                set(cell.x, cell.y, AbstractCell.makeCorner2())
            } else if (textGrid.isCorner3(cell)) {
                set(cell.x, cell.y, AbstractCell.makeCorner3())
            } else if (textGrid.isCorner4(cell)) {
                set(cell.x, cell.y, AbstractCell.makeCorner4())
            } else if (textGrid.isHorizontalLine(cell)) {
                set(cell.x, cell.y, AbstractCell.makeHorizontalLine())
            } else if (textGrid.isVerticalLine(cell)) {
                set(cell.x, cell.y, AbstractCell.makeVerticalLine())
            } else if (textGrid.isCrossOnLine(cell)) {
                set(cell.x, cell.y, AbstractCell.makeCross())
            } else if (textGrid.isStarOnLine(cell)) {
                set(cell.x, cell.y, AbstractCell.makeStar())
            }
        }
        if (DEBUG) {
            println("...the resulting AbstractionGrid is:")
            grid.printDebug()
        }
    }

    init {
        grid = TextGrid(width * 3, height * 3)
    }

    val copyOfInternalBuffer: TextGrid
        get() = TextGrid(grid)

    private fun setInternalBuffer(grid: TextGrid) {
        this.grid = grid
    }

    val width: Int
        get() = grid.width / 3
    val height: Int
        get() = grid.height / 3
    val asTextGrid: TextGrid
        get() {
            val result = TextGrid(width, height)
            for (y in 0 until grid.height) {
                for (x in 0 until grid.width) {
                    val cell = grid.Cell(x, y)
                    if (!grid.isBlank(cell)) result[x / 3, y / 3] = '*'
                }
            }
            if (DEBUG) {
                println("Getting AbstractionGrid as textGrid.\nAbstractionGrid:")
                grid.printDebug()
                println("...as text grid:")
                result.printDebug()
            }
            return result
        }
    val distinctShapes: ArrayList<CellSet>
        get() {
            val result: ArrayList<CellSet> = ArrayList<CellSet>()
            val nonBlank = grid.allNonBlank
            val distinct: ArrayList<*> = nonBlank.breakIntoDistinctBoundaries()
            val it: Iterator<*> = distinct.iterator()
            while (it.hasNext()) {
                val set = it.next() as CellSet
                val temp = AbstractionGrid(width, height)
                temp.fillCells(set)
                result.add(temp.asTextGrid.allNonBlank)
            }
            return result
        }

    protected fun fillCells(cells: CellSet?) {
        grid.fillCellsWith(cells!!, '*')
    }

    operator fun set(xPos: Int, yPos: Int, cell: AbstractCell) {
        var xPos = xPos
        var yPos = yPos
        xPos *= 3
        yPos *= 3
        for (y in 0..2) {
            for (x in 0..2) {
                if (cell.rows[x][y] == 1) {
                    grid[xPos + x, yPos + y] = '*'
                }
            }
        }
    }

    companion object {
        private const val DEBUG = false

        /**
         * Makes an AbstractionGrid using `internalGrid` as
         * its internal buffer
         *
         * @param internalGrid
         * @return
         */
        fun makeUsingBuffer(internalGrid: TextGrid): AbstractionGrid {
            require(
                !(internalGrid.width % 3 != 0
                        || internalGrid.height % 3 != 0)
            ) { "Passed TextGrid must have dimensions that are divisible by 3." }
            val result = AbstractionGrid(internalGrid.width / 3, internalGrid.height / 3)
            result.setInternalBuffer(internalGrid)
            return result
        }
    }
}